/*
 * @(#)XfspModel.java        2004/08/01
 *
 * This file is part of Xfuzzy 3.1, a design environment for fuzzy logic
 * based systems.
 *
 * (c) 2000 IMSE-CNM. The authors may be contacted by the email address:
 *                    xfuzzy-team@imse.cnm.es
 *
 * Xfuzzy is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation.
 *
 * Xfuzzy is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 */

package xfuzzy.xfsp.model;

import java.awt.Point;

import java.io.File;

import java.util.ArrayList;

import xfuzzy.xfsp.Xfsp;
import xfuzzy.xfsp.XfspEvent;

import xfuzzy.xfsp.controller.XfspController;
import xfuzzy.xfsp.controller.XfspStore;

import xfuzzy.xfsp.model.rulebases.IXfspRulebaseSimplifier;
import xfuzzy.xfsp.model.rulebases.IXfspRulebaseSimplifierFactory;
import xfuzzy.xfsp.model.rulebases.XfspRulebaseSimplifierFactory;

import xfuzzy.xfsp.model.types.IXfspTypeSimplifier;
import xfuzzy.xfsp.model.types.IXfspTypeSimplifierFactory;
import xfuzzy.xfsp.model.types.XfspTypeSimplifierFactory;

import xfuzzy.Xfuzzy;

import xfuzzy.lang.KnowledgeBase;
import xfuzzy.lang.RuleSet;
import xfuzzy.lang.Specification;
import xfuzzy.lang.Type;

import xfuzzy.util.XFileChooser;

/**
 * <p> <b>Descripci�n:</b> Modelo del sistema de simplificaci�n de sistemas
 * difusos que gestiona los distintos tipos de simplificaci�n que se pueden
 * hacer. Es completamente independiente de la representaci�n que se haga
 * mediante vistas.
 * <p> <b>E-mail</b>: <ADDRESS>joragupra@us.es</ADDRRESS>
 * @author Jorge Agudo Praena
 * @version 4.1
 * @see XfspController
 * @see XfspView
 *
 */
public class XfspModel {

  //referencia al objeto de tipo Xfuzzy que solicit� la simplificaci�n del
  //sistema
  private Xfuzzy xfuzzy;

  //especificaci�n original del sistema que permite deshacer cambios realizados
  private Specification original;

  //especificaci�n sobre la que trabajar� el modelo para llevar a cabo los
  //procesos de simplificaci�n
  private Specification workingcopy;

  //almac�n donde enviar los eventos destinados a las vistas para su
  //procesamiento por los controladores
  private XfspStore store;

  //conjunto de controladores registrados por el modelo
  private ArrayList<XfspController> controller;

  /**
   * <p> <b>Descripci�n:</b> crea una modelo a partir de un objeto de tipo
   * <b>Xfuzzy</b> indic�ndole el almac�n donde debe guardar los mensajes que
   * quiera enviar a las vistas para que puedan ser procesados por un
   * controlador.
   * @param xfuzzy Solicitante de simplificaci�n de tipos a trav�s de la
   * ventana principal de la aplicaci�n.
   * @param store Almac�n donde hay que enviar los mensajes para que puedan ser
   * procesados convenientemente por alg�n controlador.
   *
   */
  public XfspModel(Xfuzzy xfuzzy, Specification spec, XfspStore store) {
    //establece la ventana principal de la aplicaci�n que sollicit� la
    //simplificaci�n de un sistema difuso
    this.xfuzzy = xfuzzy;
    //establece la copia de trabajo para llevar a cabo los procesos de
    //simplificaci�n
    this.workingcopy = xfuzzy.duplicate(spec);
    //hace una copia del sistema original
    this.original = spec; //xfuzzy.duplicate(spec);

    //con este constructor no se necesita parser, pues ya tiene la
    //especificaci�n del sistema a simplificar
    //this.parser = null;


    //establece el almac�n donde guardar los mensajes para que sean procesados
    //por los controladores
    this.store = store;
    //inicializa a lista de controladores registrados por el modelo
    this.controller = new ArrayList<XfspController>();
  }

  /**
   * <p> <b>Descripci�n:</b> hace que el modelo registre un nuevo controlador
   * para procesar los mensajes que quiera enviar a las vistas. En caso de que
   * el controlador ya estuviera registrado por el modelo, este m�todo no hace
   * nada.
   * @param controller Nuevo controlador que debe registrar el modelo.
   *
   */
  public void registerController(XfspController controller) {
    //inicializa un booleano a falso que nos indicar� si ha encontrado el
    //controlador entre los registrados por el modelo
    boolean found = false;
    //�ndice que permitir� recorrer todos los controladores registrados por
    //el modelo hasta el momento
    int i = 0;
    //recorre todos los controladores registrados por el modelo hasta que
    //encuentre uno igual al que se quiere a�adir o bien no quede ninguno por
    //comprobar
    while ( (i < this.controller.size()) && (found == false)) {
      //si el controlador actual coincide con el que se quiere a�adir a los
      //registrados por el modelo...
      if (controller == this.controller.get(i)) {
        //...sale dell bucle de b�squeda y no hace nada m�s
        found = true;
      }
      //si el controlador actual no es igual al que se quiere a�adir...
      else {
        //...sigue buscando
        i++;
      }
    }
    //si no ha encontrador el controlador que se quiere a�adir entre los que ya
    //estaban registrados por el modelo...
    if (found == false) {
      //...a�ade el controlador nuevo a los que debe registrar el modelo
      this.controller.add(controller);
    }
  }

  /**
   * <p> <b>Descripci�n:</b> devuelve el objeto xfuzzy utilizado por el modelo
   * para hacer las copias del sistema que est� modificando.
   * @return Devuelve un objeto de la clase Xfuzzy que est� siendo utilizado
   * por el modelo para la realizaci�n de copias de sistemas.
   *
   */
  public Xfuzzy getXfuzzy() {
    return this.xfuzzy;
  }

  /**
   * <p> <b>Descripci�n:</b> establece un nuevo objeto xfuzzy para que el
   * modelo pueda realizar las copias necesarias del sistema que est�
   * modifiando.
   * @param xfuzzy Nuevo objeto de la clase Xfuzzy que ser� empleado por el
   * modelo para la realizaci�n de copias de sistemas difusos.
   *
   */
  public void setXfuzzy(Xfuzzy xfuzzy) {
    this.xfuzzy = xfuzzy;
  }

  /**
   * <p> <b>Descripci�n:</b> devuelve la copia de trabajo que est� utilizando
   * el modelo para la simplificaci�n del sistema seleccionado por el usuario.
   * @return Una copia de trabajo del sistema que se quiere simplificar.
   *
   */
  public Specification getWorkingcopy() {
    return this.workingcopy;
  }

  /**
   * <p> <b>Descripci�n:</b> establece una nueva copia de trabajo para que el
   * modelo pueda realizar su simplificaci�n.
   * @param spec Nueva copia de trabajo del sistema que se quiere simplificar
   * para que sea manipulada por el modelo.
   *
   */
  public void setWorkingcopy(Specification spec) {
    this.workingcopy = spec;
  }

  /**
   * <p> <b>Descripci�n:</b> devuelve el almac�n de eventos que est� utilizando
   * el modelo.
   * @return Almac�n de eventos que est� utilizando el modelo para que sean
   * procesados por un controlador.
   *
   */
  public XfspStore getStore() {
    return this.store;
  }

  /**
   * <p> <b>Descripci�n:</b> establece el almac�n de eventos que debe utilizar
   * el modelo.
   * @param store Nuevo almac�n de eventos que debe utilizar el modelo.
   *
   */
  public void setStore(XfspStore store) {
    this.store = store;
  }

  /**
   * <p> <b>Descripci�n:</b> indica al modelo un controlador que ya no debe
   * tener regitrado. Si el controlador indicado no estaba siendo registrado por
   *  el modelo, este m�todo no hace nada.
   * @param controller Controlador que debe dejar de tener registrado el modelo.
   *
   */
  public void deleteController(XfspController controller) {
    //busca entre todos los controladores registrados por el modelo
    for (int i = 0; i < this.controller.size(); i++) {
      //si encuentra un controlador igual al indicado...
      if (controller == this.controller.get(i)) {
        //...lo elimina de la lista de controladores que tiene el modelo
        //registrados
        this.controller.remove(i);
      }
    }
  }

  /**
   * <p> <b>Descripci�n:</b> permite obtener la especificaci�n de un sistema
   * difuso con la que est� trabajando el modelo actualmente.
   * @return Especificaci�n con la que trabaja el modelo para lograr su
   * simplificaci�n.
   *
   */
  public Specification getSpecification() {
    //devuelve la especificaci�n con la que le modelo est� trabajando
    return workingcopy;
  }

  /**
   * <p> <b>Descripci�n:</b> simplifica un tipo mediante el m�todo indicado y
   * tomando los argumentos establecidos por el usuario.
   * @param type Tipo que se quiere simplificar de la espeficiaci�n con la que
   * trabaja el modelo.
   * @param method M�todo de simplificaci�n de tipos que se debe utilizar.
   * @param args Argumentos que se deben utilizar para la simplificaci�n del
   * tipo.
   *
   */
  public void simplifyType(Type type, String method, Object[] args) {
    //n�mero inicial de funciones de pertenencia que tiene el tipo antes de su
    //simplificaci�n
    int initialMFs = type.getMembershipFunctions().length;
    //simplificador de tipos de un sistema difuso
    IXfspTypeSimplifier ts;
    //crea un f�brica de objetos simplificadores de tipos
    IXfspTypeSimplifierFactory f = XfspTypeSimplifierFactory.getInstance();
    //obtiene de la f�bria un simplificador de tipos seg�n el m�todo deseado
    //y que utilice los argumentos indicados
    ts = f.create(method, args);
    //ordena al simplificador de tipos que simplifique el tipo elegido y
    //actualice la especificaci�n con la que trabaja el modelo
    ts.simplify(type, this.workingcopy);
    //n�mero de funciones de pertenencia que tiene el tipo despu�s de su
    //simplificaci�n
    int finalMFs = type.getMembershipFunctions().length;
    //crea los argumentos para el refresco de las vistas...
    Object[] refreshArgs = new Object[3];
    //...el primero de ellos son los tipos...
    refreshArgs[0] = this.workingcopy.getTypes();
    //...el segundo las bases de reglas...
    refreshArgs[1] = this.workingcopy.getRulebases();
    //...y el tercero el mensaje que se debe mostrar
    String[] msg = {
        "Process finished.",
        initialMFs + " membership functions reduced to " + finalMFs};
    refreshArgs[2] = msg;
    //crea un nuevo evento que indica que las vistas deben regenerar el gr�fico
    //de simplificaci�n de tipos...
    XfspEvent ev = new XfspEvent("Refresh", refreshArgs);
    //...y lo almacena para que alg�n controlador pueda procesarlo
    store.store(ev);
  }

  /**
   * <p> <b>Descripci�n:</b> simplifica una base de reglas mediante el m�todo
   * indicado y tomando los argumentos establecidos por el usuario.
   * @param rulebase Base de reglas que se quiere simplificar de la
   * espeficiaci�n con la que trabaja el modelo.
   * @param method M�todo de simplificaci�n de tipos que se debe utilizar.
   * @param args Argumentos que se deben utilizar para la simplificaci�n del
   * tipo.
   *
   */
  public void simplifyRulebase(KnowledgeBase rulebase, String method, Object[] args) {
    //n�mero inicial de reglas que tiene la base de reglas antes de su
    //simplificaci�n
    int initialRules = ((RuleSet) rulebase).getRules().length;
    //crea un f�brica de objetos simplificadores de bases de reglas
    IXfspRulebaseSimplifierFactory f = XfspRulebaseSimplifierFactory.
        getInstance();
    //obtiene de la f�bria un simplificador de bases de reglas seg�n el m�todo
    //deseado y que utilice los argumentos indicados
    IXfspRulebaseSimplifier s = f.create(method, args);
    //ordena al simplificador de bases de reglas que simplifique la base de
    //reglas elegida y actualice la especificaci�n con la que trabaja el modelo
    s.simplify(rulebase, this.workingcopy);
    //n�mero de reglas que tiene la base de reglas despu�s de su simplificaci�n
    int finalRules = ((RuleSet) rulebase).getRules().length;
    //crea los argumentos para el refresco de las vistas...
    Object[] refreshArgs = new Object[3];
    //...el primero de ellos son los tipos...
    refreshArgs[0] = this.workingcopy.getTypes();
    //...el segundo las bases de reglas...
    refreshArgs[1] = this.workingcopy.getRulebases();
    //...y el tercero el mensaje que se debe mostrar
    String[] msg = {
        "Process finished.",
        initialRules + " rules transformed into " + finalRules +
        " equivalent rules"};
    refreshArgs[2] = msg;
    //crea un nuevo evento que indica que las vistas deben regenerar el listado
    //reglas en la ventada de simplificaci�n de bases de reglas...
    XfspEvent ev = new XfspEvent("Refresh", refreshArgs);
    //...y lo almacena para que alg�n controlador pueda procesarlo
    store.store(ev);
  }

  /**
   * <p> <b>Descripci�n:</b> almacena el sistema con el que est� trabajando el
   * modelo de forma permanente en un fichero.
   *
   */
  public void save() {
    //obtiene el fichero correspondiente a la especificaci�n original del
    //sistema
    File root = original.getFile();
    //si no se puede obtener dicho fichero...
    if (root == null) {
      //...obtiene el directorio de trabajo de la aplicaci�n
      root = xfuzzy.getWorkingDirectory();
    }
    //crea un nuevo selector de ficheros
    XFileChooser chooser = new XFileChooser(root, XFileChooser.SAVE);
    int returnVal = chooser.showDialog();
    //si no se ha seleccionado ning�n fichero...
    if (returnVal != XFileChooser.SELECTED) {
      //...no hace nada
      return;
    }
    if (returnVal == XFileChooser.SELECTED) {
      //obtiene el fichero seleccionado
      File file = chooser.getSelectedFile();
      //guarda la copia de trabajo del sistema en el fichero seleccionado
      workingcopy.save_as(file);
      //carga el nuevo fichero en el sistema xfuzzy
      xfuzzy.load(file);
    }
  }

  /**
   * <p> <b>Descripci�n:</b> aplica al sistema original los cambios que se han
   * realizado sobre el sistema en el que trabaja el modelo.
   *
   */
  public void apply() {
    //duplica la especificaci�n sobre la que trabaja el sistema
    Specification dup = xfuzzy.duplicate(workingcopy); //spec);
    //establece en el modelo original el conjunto de operadores del sistema
    //duplicado...
    original.setOperatorsets(dup.getOperatorsets());
    //...el conjunto de tipos...
    original.setTypes(dup.getTypes());
    //...las bases de reglas...
    original.setRulebases(dup.getRulebases());
    //...y los m�dulos del sistema
    original.setSystemModule(dup.getSystemModule());
    //indica que el sistema original ha sido modificado
    original.setModified(true);
  }

  /**
   * <p> <b>Descripci�n:</b> reestablece el sistema original sobre la copia de
   * trabajo del modelo.
   * @param location Localizaci�n en la pantalla que debe ocupar la vista tras
   * reestablecer el sistema original.
   *
   */
  public void reload(Point location) {
    //crea un nuevo evento que solicita el cierre del sistema...
    XfspEvent ev = new XfspEvent("Close",null);
    //...y lo almacena en el almac�n
    store.store(ev);
    //crea un nuevo sistema de simplificaci�n...
    Xfsp newInstance = new Xfsp(this.xfuzzy, this.original, location);
    //...y lo muestra en pantalla
    newInstance.show();
  }

  /**
   * <p> <b>Descripci�n:</b> termina la simplificaci�n del sistema devolviendo
   * el sistema con el que se estaba trabajando al estado que ten�a el sistema
   * original.
   *
   */
  public void close() {
    //cambia el conjunto de operadores de la especificaci�n del sistema con el
    //que estaba trabajando el modelo por el del sistema original...
    workingcopy.setOperatorsets(original.getOperatorsets());
    //...el conjunto de tipos utilizados...
    workingcopy.setTypes(original.getTypes());
    //...el conjunto de bases de reglas...
    workingcopy.setRulebases(original.getRulebases());
    //...y el conjunto de m�dulos del sistema
    workingcopy.setSystemModule(original.getSystemModule());
  }
}
